import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';

import { Observable, of } from 'rxjs';
import { catchError, map, tap } from 'rxjs/operators';

import { environment } from 'src/environments/environment';

import { Reply } from '../entity/Reply';
import { User, BookedWrapper, Booked, Settings } from '../entity/TT';
import { Avatar } from '../entity/Avatar';
import { Calendar, Params } from '../entity/Calendar';

const httpOptions = {
  headers: new HttpHeaders({
    'Content-Type': 'application/json'
  }),
  params: null,
  withCredentials: true,
};


@Injectable({
  providedIn: 'root'
})
export class RequestReplyService {


  private meUrl;
  private passwordUrl;

  
  private getRtcUrl;

  private getSetUrl;
  private saveSetUrl;

  private getUserUrl;
  private getConferenceUrl;
  private giveCoinUrl;
  private giveFabulousUrl;
  private bookedCurrentUrl;
  private bookedExitUrl;

  private recordListBookedUrl;
  private recordListMessageUrl;

  private calendarUrl;

  private loginUrl;
  private logoutUrl;

  showMaterialUrl;


  me: User;
  avatar: Avatar;
  conferenceId: string;

  constructor(private http: HttpClient) {
    console.log('environment', environment);
    this.init(environment);
   }

   init(env) {
    console.log('init', env);
    const apiUrl = env.apiUrl;

    this.meUrl             = apiUrl + 'me/get/info';
    this.passwordUrl       = apiUrl + 'me/get/accessToken';
    this.getRtcUrl         = apiUrl + 'init/get/rtc';

    this.getSetUrl         = apiUrl + 'settings/load'
    this.saveSetUrl         = apiUrl + 'settings/save'

    this.getUserUrl        = apiUrl + 'user/get';
    this.getConferenceUrl  = apiUrl + 'conference/current';
    this.giveCoinUrl       = apiUrl + 'gift/give/coin';
    this.giveFabulousUrl   = apiUrl + 'gift/give/fabulous';
    this.bookedCurrentUrl  = apiUrl + 'booked/current';
    this.bookedExitUrl     = apiUrl + 'booked/exit';

    this.recordListMessageUrl   = apiUrl + 'record/list/message';

    this.calendarUrl            = apiUrl + 'calendar/load';
    this.recordListBookedUrl    = apiUrl + 'calendar/list/booked';

    this.showMaterialUrl        = apiUrl + 'material/preview';


    this.loginUrl = env.baseUrl   +  '/api/ua/passport/login';
    this.logoutUrl = env.baseUrl  +  '/api/ua/passport/logout';

   }

   GetBaseUrl() {
    return environment.baseUrl;
   }

   Logout() {
    return this.GetReply(this.logoutUrl);
   }

  GetAccessToken(param: { loginId: string; password: string }) {
    return this.PostReply(this.loginUrl, null, param);
  }

  GetRtc(): Observable<any> {
    return this.GetReply(this.getRtcUrl).pipe(
      map(reply => {
        if (reply && reply.success) {
          return reply.data;
        } else {
          console.error('error', reply);
        }
      })
    );
  }

  GetSetting(): Observable<Settings> {
    return this.GetReply(this.getSetUrl).pipe(
      map(reply => {
        if (reply && reply.success) {
          return reply.data;
        } else {
          console.error('error', reply);
        }
      })
    );
  }

  SaveSetting(set : Settings):Observable<Reply> {
    return this.PostReply(this.saveSetUrl, null, set)
  }

  GetCalendar(params: Params): Observable<Calendar> {
    return this.GetReply(this.calendarUrl, params).pipe(
      map(reply => {
        if (reply && reply.success) {
          return reply.data;
        } else {
          console.error('error', reply);
        }
      })
    );
  }

  ExitBooked(booked: Booked) {
    return this.GetReply(this.bookedExitUrl, {sn: booked.sn}).pipe(
      map(reply => {
        if (reply && reply.success) {
          return reply.data;
        }
      })
    );
  }

  GetCurrentBookedList(): Observable<BookedWrapper> {
    return this.GetReply(this.bookedCurrentUrl).pipe(
      map(reply => {
        if (reply && reply.success) {
          const bookeds = reply.data;

          const w: BookedWrapper = {current: bookeds[0] || null, next: bookeds[1] || null, delay: reply.extra.delay || null};
          return w;
        }
      })
    );
  }

  GetBookedList(params: any): Observable<any> {
    return this.GetReply(this.recordListBookedUrl, params ).pipe(
      map(reply => {
        if (reply && reply.success) {
          const bookeds = reply.data;
          return bookeds;
        }
      })
    );
  }

  GetBookedMessage(booked: Booked): Observable<any[]> {
    return this.GetReply(this.recordListMessageUrl, {sn: booked.sn }).pipe(
      map(reply => {
        if (reply && reply.success) {
          return reply.data;
        }
      })
    );
  }

  GiveCoin(participants: string[], amount: number): Observable<boolean> {
    return this.GetReply(this.giveCoinUrl, {participants, amount, conferenceId: this.conferenceId})
    .pipe(map((reply) => {
      if (reply && reply.success) {
        const data = reply.data;
        return data;
      }
    }));
  }

  GiveFabulous(participants: string[], amount: number): Observable<boolean> {
    return this.GetReply(this.giveFabulousUrl, {participants, amount, conferenceId: this.conferenceId})
    .pipe(map((reply) => {
      if (reply && reply.success) {
        const data = reply.data;
        return data;
      }
    }));
  }

  GetPassword(): Observable<string> {
    return this.GetReply(this.passwordUrl)
          .pipe(map((reply) => {
            if (reply && reply.success) {
              const data = reply.data;
              return data;
            }
          }));
  }

  LoadMe(): User {
    return this.me;
  }

  GetMe(): Observable<User> {
    return this.GetReply(this.meUrl)
          .pipe(map((reply) => {
            if (reply && reply.success) {
              const data = reply.data;
              const str = JSON.stringify(data);
              this.me  = JSON.parse(str);
              this.avatar = Avatar.from(this.me);
              return this.me;
            }
          }));
  }

  GetUser(uin: string): Observable<User> {
    return this.GetReply(this.getUserUrl, {uin})
          .pipe(map((reply) => {
            if (reply && reply.success) {
              const data = reply.data;
              const str = JSON.stringify(data);
              const u: User  = JSON.parse(str);
              return u;
            }
          }));
  }


  GetConference(): Observable<string> {
    return this.GetReply(this.getConferenceUrl)
    .pipe(map((reply) => {
      if (reply && reply.success) {
        this.conferenceId = reply.data;
        return this.conferenceId;
      }
    }));
  }


  GetReply(url: string, params?: object): Observable<Reply> {
    this.log('GetReply:', url, params);

    if (params) {
        let hps = new HttpParams();
        for (const k in params) {
          hps = hps.set(k, params[k]);
        }
        httpOptions.params = hps;
    }

    console.log('GetReply', url);

    return this.http.get<Reply>(url, httpOptions)
      .pipe(
        tap(_ => this.log('fetched Reply:', _)),
        catchError(this.handleError<Reply>('GetReply', null))
      );
  }

  PostReply(url: string, params?: object, body?: object): Observable<Reply> {
    this.log('PostReply:', url, params, body);
    if (params) {
      let hps = new HttpParams();
      for (const k in params) {
        hps = hps.set(k, params[k]);
      }
      httpOptions.params = hps;
    }

    return this.http.post<Reply>(url, body, httpOptions)
      .pipe(
        tap(_ => this.log('fetched Reply:', _)),
        catchError(_=>this.handleError<Reply>('PostReply', _))
      );
  }

  private handleError<T>(operation = 'operation', result?: T) {
    return (error: any): Observable<T> => {

      // TODO: send the error to remote logging infrastructure
      console.error(`${operation} handleError:`, error);

      console.dir(error.headers.get('location')); // log to console instead

      // log to console instead

      // TODO: better job of transforming error for user consumption
      // this.log(`${operation} failed: ${error.message}`);

      // Let the app keep running by returning an empty result.
      return of(result as T);
    };
  }


  private log(message: string, ...obj: any[]) {
    console.log('msg:', message, obj);
  }


}

